import json
import random

from flask import current_app, request, g
from flask_restful import Resource
from flask_restful.reqparse import RequestParser

from common.models import db
from common.models.account import Account
from common.models.user import User
from common.utils.Financial_Redis import fr
from common.utils.decorator import login_required
from common.utils.sms_code_send import sms_send
from common.utils.tokens_pyjwt import generate_tokens, verify_token
from financial.resource.user import constants
from financial.resource.user.constants import SMS_CODE_EXPIRES


class Register(Resource):
    '''
    用户注册的视图
    '''

    def post(self):
        rp = RequestParser()
        rp.add_argument('username', required=True)
        rp.add_argument('password', required=True)
        rp.add_argument('phone', required=True)
        rp.add_argument('code', required=True)
        rp.add_argument('invite_code')

        args = rp.parse_args()
        username = args.username
        password = args.password
        phone = args.phone
        code = args.code
        invite_code = args.invite_code

        # 验证用户名是否唯一
        u = User.query.filter(User.username == username).first()
        if u:  # 如果用户名存在
            current_app.logger.info('{}:用户名已经存在，请更换用户名'.format(username))
            return {'message': '用户名重复', 'code': 20001}

        # 从数据库中获取之前保存的验证码
        real_code = fr.get('registerCode:{}'.format(phone))
        if not real_code or real_code.decode() != code:  # 数据库中没有code或者存储的code和参数不一样
            current_app.logger.info('验证码错误或者失效')
            return {'message': '验证码错误或者失效', 'code': 20001}

        # 验证和关联邀请码
        def check_invite(self, user, invite_code):
            code = invite_code.strip()
            invite_user = User.query.filter(User.inviteId == code).first()  # 查找是否有邀请码为code的用户
            if invite_user:  # 如果有
                user.invite_user_id = invite_user.id  # 将邀请此用户的用户的id存入到此用户的invite_user_id中
                invite_user.accountInfo.discount += constants.INVITE_MONEY  # 邀请用户的账户中增加50元的代金券
                invite_user.sumFriends = invite_user.sumFriends + 1 if invite_user.sumFriends else 1  # 邀请的记录数 增加一个

        # 将用户保存到数据库中
        # 需要注意的是，在将用户保存到数据库的同时，还要创建与其对应的账户,所以要用到数据库的事务处理
        u = User(username=username, pwd=password, phone=phone)
        if invite_code:
            check_invite(u, invite_code)
        try:
            db.session.add(u)  # 这一步并没有将数据插入到数据库中，就没有产生id,account也就没有办法根据用户id创建
            db.session.flush()  # 将数据插入到数据库的缓冲区(可以得到自增ID)。并不是插入到数据库中。
            account = Account(userId=u.id)
            db.session.add(account)
            db.session.commit()  # 现在提交
            return {'msg': 'success'}
        except Exception as ex:
            current_app.logger.error(ex)
            db.session.rollback()  # 回滚，向数据库中插入用户和账户同时失败
            return {'message': '用户注册时，插入数据库失败', 'code': 20001}


class SmsCode(Resource):
    '''
    发送短信验证码，并存储到redis数据库中
    '''

    def get(self):
        phone = request.args['phone'].strip()  # 获得手机号并去除空格
        code = random.randint(100000, 1000000)
        r = sms_send(phone, code)  # 返回值是一个json字符串

        # 验证码需要存起来，存放到redis数据库中，还需要把验证码在注册的请求中去验证。
        # setex  函数有三个参数：1、key  2.时效，  3. 值
        if json.loads(r).get('code') == 2:  # 验证码发送成功以后的返回值为2
            try:
                fr.setex('registerCode:{}'.format(phone), SMS_CODE_EXPIRES, code)
            except Exception as e:
                current_app.logger.info(e)
            return {'msg': '验证码发送成功', 'code': 200}


class Login(Resource):
    def post(self):
        rp = RequestParser()
        rp.add_argument('username', required=True)
        rp.add_argument('password', required=True)

        args = rp.parse_args()
        username = args.username  # 用户
        password = args.password  # 密码

        user = User.query.filter(User.username == username).first()
        if user:
            # 验证密码是否正确
            if user.check_password(password):
                # 用户登陆成功，之后产生一个token,以便于后面去认证token
                token = generate_tokens(user.id)
                current_app.logger.info(token)
                current_app.logger.info('测试验证token', verify_token(token))
                return {'token': token}
        return {'message': '用户名或者密码错误', 'code': 20001}

class Logout(Resource):
    '''
    退出登录。使用登录拦截的装饰器
    '''
    method_decorators = [login_required]
    def post(self):
        if g.user_id:
            g.user_id=None
            return {'msg':'成功退出登录'}